/*global $,jQuery,window,LOCK5MAIN*/

$( function() {
    // global objects
    LOCK5MAIN.histogramTypes = ["mean_difference"];
    LOCK5MAIN.histograms = {};
    $.each(LOCK5MAIN.histogramTypes, function(i, type) {
        LOCK5MAIN.histograms[type] = $.createHistogram();
        LOCK5MAIN.histograms[type].type = type;
        LOCK5MAIN.histograms[type].two_plot = true;
    });
    LOCK5MAIN.activeHistogram = LOCK5MAIN.histograms.mean_difference;
    LOCK5MAIN.hypothesisTest = true;

    /* Methods */
    /*
     * LOCK5MAIN.clearGraph Clears everything to do with the LOCK5MAIN
     * randomization graph including data and control settings.
     *
     * Returns true
     */
    LOCK5MAIN.clearGraph = function() {
        $.each(LOCK5MAIN.histograms, function(i, h) {
            h.clear();
        });
        LOCK5MAIN.dotPlot.setBounds({
            xaxis: LOCK5MAIN.ORIGINAL_BOUNDS.xaxis,
            yaxis: LOCK5MAIN.ORIGINAL_BOUNDS.yaxis});
        LOCK5MAIN.doublePlotProperties = null;
        LOCK5MAIN.dotPlot.clear();
        LOCK5MAIN.doublePlot("#current-sample-plot", []);
        return true;
    };
    /*
     * hoverGraph: Function that is called when the mouse hovers over a
     * point on the dotplot
     *
     * parameters: id (String) the css id of the element to plot over
     */
    LOCK5MAIN.hoverGraph = function(id) {
        return function(event, pos, item) {
            if (item) {
                var generatedFrom = item.series.data[item.dataIndex][2];
                LOCK5MAIN.doublePlot(id, generatedFrom, {
                    labels: LOCK5MAIN.ORIGINAL_SAMPLE.label,
                    useOldAxes: false
                });
            }
        };
    };
    /*
     * LOCK5MAIN.generateNRandomizationSamples: generates statistics from the given
     * points
     *
     * returns: Array of statistics. A statistic is an object containing
     * statistics for each input function type as well as the bootstrap sample
     * that generated it
     *
     * parameters: point (Array): the original data to sample n (Integer):
     * nubmer of points to generate
     * statFunction (function): calculates the statistic
     */
    LOCK5MAIN.generateNRandomizationSamples = function(points, n, statfunction) {
        var pointNumber, sampleNumber, randValue, tmpPoints, statistics = [], slope, correlation, errorsSoFar, MAXERRORS = 100,means;
        // generate n randomization samples
        means=[0,0];
        //determine means
        $.each(points, function(i,series) {
            $.each(series, function(j,val) {
                means[i] = means[i] + val;
            });
            means[i] = means[i]/series.length;
        });
        for (sampleNumber = 0; sampleNumber < n; sampleNumber += 1) {
            // sometimes a boostrap sample
            // generates a degenerate sample resulting in a failure on
            // one of the statistic functions. we allow MAXERRORS of these
            // to occur before we barf. The while loop keeps it going
            // but if a statistic is calculated the loop breaks
            errorsSoFar = 0;
            while (true) {
                try {
                    tmpPoints = [[],[]];
                    // generate points.length random samples
                    tmpPoints = LOCK5MAIN.sampleFunction(points,means);
                    statistics.push({
                        mean_difference: statfunction(tmpPoints[0],tmpPoints[1]),
                        generatedFrom: tmpPoints
                    });
                    break;
                } catch (err) {
                    errorsSoFar += 1;
                    if (errorsSoFar > MAXERRORS) {
                        throw ("Cannot generate samples, statistic function generates " + err);
                    }
                }
            }
        }
        return statistics;
    };
    /*
     * generateAndPlotBootstrapSample: helper function to generate n additional
     * points and plot the current total.
     *
     * returns: true
     *
     * parameters: point (Array): the original data to sample n (Integer):
     * nubmer of points to generate slopeFunction (function): calculates the
     * slope statistic from the boostrap points correlationFunction (function):
     * calculates the correlation statistic from the boostrap points
     */
    LOCK5MAIN.generateAndPlotBootstrapSample = function(n) { //
        return function() {

            // calculate samples
            var statistics = LOCK5MAIN.generateNRandomizationSamples(LOCK5MAIN.ORIGINAL_SAMPLE.data, n, $.differenceInMeans);

            // add those samples to each histogram
            $.each(LOCK5MAIN.histograms, function(i, h) {
                h.addPoints(statistics);
            });
            // plot the bootstrap plot with the old and new samples from
            // the current active histogram
            LOCK5MAIN.dotPlot.plot("#dotplot", LOCK5MAIN.activeHistogram.histogramPoints);

            // Plot the most recent statistic in the hoverplot so it animates
            // as single stats are added at a time. Doesn't make sense if
            // many points are added, but still keep it for uniformity
            LOCK5MAIN.doublePlot("#current-sample-plot", statistics[0].generatedFrom, {
                labels: LOCK5MAIN.ORIGINAL_SAMPLE.label,
                useOldAxes: false
            });

            return true;
        };
    };
    /*
     * LOCK5MAIN.initialize(data,domain,range) Initializes both plots based on
     * the 2D set of points passed to it.
     *
     * returns None
     *
     * Parameters data (Array): an N dimensional array made up 2D arrays
     * representing original sample points domain (String): the label of the
     * domain range (String): the label of the range label (String): the label
     * of the data to display to a user
     */
    LOCK5MAIN.initialize = function(data, variable, label) {
        var bootstrapGraphWidth, bootstrapGraphHeight, samplePoints;

        LOCK5MAIN.ORIGINAL_SAMPLE.data = data;
        LOCK5MAIN.ORIGINAL_SAMPLE.variable = variable;
        LOCK5MAIN.ORIGINAL_SAMPLE.label = label;

        //replace default hover function on smaller plots
        $("#original-sample-plot").unbind("plothover", LOCK5MAIN.hoverPoint);
        $("#current-sample-plot").unbind("plothover", LOCK5MAIN.hoverPoint);
        $("#original-sample-plot").bind("plothover", LOCK5MAIN.hoverXValue);
        $("#current-sample-plot").bind("plothover", LOCK5MAIN.hoverXValue);

        // LOCK5MAIN.PIXELSPERDOT and physical width of graph determine
        // histogram buckets
        bootstrapGraphHeight = parseInt($("#dotplot").height(), 10);
        bootstrapGraphWidth = parseInt($("#dotplot").width(), 10);

        // bootstrap some initial points to determine the initial xaxis
        // bounds on graph
        samplePoints = LOCK5MAIN.generateNRandomizationSamples(LOCK5MAIN.ORIGINAL_SAMPLE.data, 1000, $.differenceInMeans);

        // set up ranges on each histogram
        $.each(LOCK5MAIN.histograms, function(type, h) {
            ( function(array) {
                h.max = $.selection(array, 0);
                h.min = $.selection(array, array.length - 1);
            }($.map(samplePoints, function(sample) {
                return sample[type];
            })));
        });
        LOCK5MAIN.ORIGINAL_BOUNDS = {
            xaxis: {
                //histogram sorts points backwards
                min: LOCK5MAIN.activeHistogram.max,
                max: LOCK5MAIN.activeHistogram.min
            },
            yaxis: {
                min: 0,
                max: Math.floor(parseInt($("#dotplot").height(), 10) / LOCK5MAIN.PIXELSPERDOT)
            }
        };
        LOCK5MAIN.dotPlot.setBounds({
            xaxis: LOCK5MAIN.ORIGINAL_BOUNDS.xaxis,
            yaxis: LOCK5MAIN.ORIGINAL_BOUNDS.yaxis});

        $.each(LOCK5MAIN.histograms, function(key, histogram) {
            histogram.k = Math.floor(bootstrapGraphWidth / LOCK5MAIN.PIXELSPERDOT);
        });
        LOCK5MAIN.doublePlot("#original-sample-plot", LOCK5MAIN.ORIGINAL_SAMPLE.data, {
            labels: LOCK5MAIN.ORIGINAL_SAMPLE.label
        });

        LOCK5MAIN.dotPlot.plot("#dotplot", []);

        LOCK5MAIN.doublePlot("#current-sample-plot", []);

        LOCK5MAIN.clearGraph();

    };
    /* Initialize the application*/
    $( function() {
        var sample1, sample2, sample3, menuItems;
        sample1 = function (points) {
            //permute labels
            var permutedPoints = [[],[]], i, j, swapI, swapJ, swapIndex, bucket, len;
            for (i = 0; i <= 1; i += 1) {
                for (j = 0; j < points[i].length; j += 1) {
                    permutedPoints[i][j] = points[i][j];
                }
            }
            try {
                len = points[0].length;
                for (i = 0; i <= 1; i += 1) {
                    for (j = 0; j < len; j += 1) {
                        if (i === 1 && j === len-1) {
                            break;
                        }
                        swapIndex = i*len+j+Math.floor(Math.random()*(points[0].length+points[1].length-(i*len+j)));
                        if (swapIndex < points[0].length) {
                            swapI = 0;
                            swapJ = swapIndex;
                        } else {
                            swapI = 1;
                            swapJ = swapIndex - points[0].length;
                        }
                        bucket = permutedPoints[swapI][swapJ];
                        permutedPoints[swapI][swapJ] = permutedPoints[i][j];
                        permutedPoints[i][j] = bucket;
                    }
                }
            } catch(e) {
                console.error(e);
            }
            return permutedPoints;
        };
        sample2 = function (points, means) {
            //shift points to - their means, then sample w/ replacement
            var shiftedPoints = [[],[]], i, j, index;
            //shift means
            for (i = 0; i <= 1; i += 1) {
                for (j = 0; j < points[i].length; j += 1) {
                    index = Math.floor(Math.random()*(points[i].length));
                    shiftedPoints[i][j] = points[i][index]-means[i];
                }
            }
            return shiftedPoints;
        };
        sample3 = function (points) {
            //sample with replacement from the pooled data, no shifting
            var sampledPoints = [[],[]], i, j, sampleI, sampleJ, sampleIndex;
            for (i = 0; i <= 1; i += 1) {
                for (j = 0; j <= points[i].length; j += 1) {
                    sampleIndex = Math.floor(Math.random()*(points[0].length+points[1].length));
                    if (sampleIndex < points[0].length) {
                        sampleI = 0;
                        sampleJ = sampleIndex;
                    } else {
                        sampleI = 1;
                        sampleJ = sampleIndex - points[0].length;
                    }
                    sampledPoints[i][j] = points[sampleI][sampleJ];
                }
            }
            return sampledPoints;
        };
        menuItems = [{
            label: "Reallocate Groups",
            callback: function() {
                //permute group labels
                LOCK5MAIN.sampleFunction = sample1;
                LOCK5MAIN.clearGraph();
            }
        },{
            label: "Shift Groups",
            callback: function() {
                //shift datasets to common mean sample with replacement from within respective datasets
                LOCK5MAIN.sampleFunction = sample2;
                LOCK5MAIN.clearGraph();
            }
        },{
            label: "Combine Groups",
            callback: function() {
                //sample with replacement from the pooled data, no shifting
                LOCK5MAIN.sampleFunction = sample3;
                LOCK5MAIN.clearGraph();
            }
        }
        ];
        $.createPopupMenu($("#sample-function-button"), menuItems);
        LOCK5MAIN.sampleFunction = sample1;
        LOCK5MAIN.startup({
            sampleType: "Bootstrap",
            generateAndPlot: LOCK5MAIN.generateAndPlotBootstrapSample,
            graphAnnotation: function(arrowAnnotationFn, plot, xyList) {
                var meanX = $.mean(xyList, 3), meanText = Math.truncate(meanX, 3);
                arrowAnnotationFn(plot, meanX, meanText, "#000");
            },
            parseCSV_two_datasets: true
        });

    });
});